package testJdk.commonUse.string;

import org.junit.Test;

/**
 * @author keyer
 * @Description : 描述
 * Created by cyd on 2021-04-23 16:09:26
 */
public class StringDeepTest {
    @Test
    public void test01() {
        // 直接以字面量形式声明的str，其数据存在字符串常量池（在堆空间）
        String str1 = "hello";
        String str2 = "hello";
        System.out.println(str1 == str2);

        // String是引用传递，但是由于其不可变性，做改变时，其实是新创建了一个字符串（在常量池中）
        str2 = str2 + " world";
        System.out.println(str1 == str2);
    }

    @Test
    public void test02() {
        String str1 = "hello";
        // 以new形式声明的str，其数据存在堆空间，是一个单独的对象

        String str2 = new String("hello");
        System.out.println(str1 == str2);
        /*
        String使用private final char value[]来实现字符串的存储,
        也就是说String对象创建之后,就不能再修改此对象中存储的字符串内容,
        就是因为如此,才说String类型是不可变的(immutable)。

        String类有一个特殊的创建方法,就是使用""双引号来创建.例如new String("i am")
        实际创建了2个String对象,一个是"i am"通过""双引号创建的,另一个是通过new创建的.
        只不过他们创建的时期不同,一个是编译期(常量池中的String字面量),一个是运行期（堆区中的String对象）!
         */
    }

    @Test
    public void test03() {
        String str1 = new String("hello");
        String str2 = new String("hello");
        // 以new形式声明的str，即使有相同的数据，也是封装在不通的对象中，放在不同的内存地址
        System.out.println(str1 == str2);
    }

    @Test
    public void Test04() {
        Integer i1 = new Integer(1);
        Integer i2 = new Integer(1);
        //i1,i2对象具有不同的内存地址
        System.out.println(i1 == i2);

        Integer i3 = 1;
        Integer i4 = 1;
        //i3,i4指向常量池中同一个字面值，即同一个内存位置，因此下面语句返回true
        System.out.println(i3 == i4);

        //而i1,i3位于不同的内存位置，会返回false
        System.out.println(i1 == i3);
    }

    @Test
    public void test05() {
        // 只有大于127或者小于-128，，直接声明字面量才会用常量池
        Integer i1 = 1111;
        Integer i2 = 1111;
        //由于常量值对于127，不会使用常量池技术，因此输出false
        System.out.println(i1 == i2);
        //但是两个变量的内容是相等的，因此输出true
        System.out.println(i1.equals(i2));
    }

    @Test
    public void test06() {
        int num1 = 1;
        int num2 = num1;

        System.out.println(num1 == num2);

        num2 = num2 + 1;
        System.out.println(num1);
    }

    @Test
    public void test07() {
        // 直接以字面量形式声明的字符串变量，直接指向常量池
        String a = "a1";
        String b = "a" + 1;
        System.out.println(a == b);
    }

    @Test
    public void test08() {
        // 使用了final，编译器能确定使用常量池
        final String a = "a";
        final String b = "b";
        String ab = "ab";
        System.out.println(ab == a + b);

        // 编译器不能确定是否是常量
        String c = "c";
        String d = "d";
        String cd = "cd";
        System.out.println(cd == c + d);
        /*
          这里c和d都指向常量池，但c+d不是，c+d相当于一个匿名对象，
          上述代码等价于:
            String s = c + d;
            System.out.println(cd == s);
         */
    }

    /**
     * final类型的字符串，直接指向常量池，拼接这种字符串会在常量池中生成新的字符串
     */
    @Test
    public void test09() {
        final String a = "a";
        final String b = "b";
        final String ab = "ab";
        System.out.println(ab == a + b);
    }

//
//    public static String getB() {
//        return "b";
//    }
//
//    public static String getFinalB() {
//        final String b = "b";
//        return b;
//    }

    /**
     * 非final类型不会加入常量池，但是intern方法可以将字符串添加进常量池
     */
    @Test
    public void test11() {
        String a = "a";
        String b = "b";
        // 默认指向常量池
        String ab = "ab";
        System.out.println(ab == a + b);
        // intern方法可以将字符串添加进常量池，并返回直接指向常量池的引用
        System.out.println(ab == (a + b).intern());
    }

    /**
     * 测试字节数组的可变性 和 字符串的不可变性
     */
    @Test
    public void test12() {
        String str = new String("hello");
        char[] chars = {'t', 'e', 's', 't'};
        // 字符串是不可变类，对它做出改变会导致生成一个新的字符串
        change(str, chars);
        // str的指向还是原来的字符串，所以输出不会变
        System.out.println(str);
        System.out.println(chars);

    }

    private void change(String str, char[] chars) {
        str = "test ok";
        chars[0] = 'b';
    }
}


